package com.ug_project.dict.engine.indextable

import org.junit.Assert.assertEquals
import org.junit.Test
import java.nio.channels.FileChannel
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardOpenOption

class IndexTableTest {

    val file = Paths.get("temp.wdt")

    fun provideIndexTable(action: (table: IndexTable) -> Unit) {
        if (Files.exists(file)) Files.delete(file)
        Files.createFile(file)

        val channel = FileChannel.open(file, StandardOpenOption.WRITE, StandardOpenOption.READ)
        val table = IndexTable(channel)
        action(table)
        channel.close()
        Files.deleteIfExists(file)
    }

    @Test
    fun pointerBlock() {
        provideIndexTable { table ->

            for (i in 1..5) {
                table.allocateBlock {
                    PointerBlock(table, it).apply {
                        this.fourthLevelAddress = i
                        this.fifthLevelAddress = i
                        this.sixthLevelAddress = i
                        this.seventhLevelAddress = i

                        writeToChannel()
                    }
                }
            }
            for (i in 0..4) {
                assertEquals(table.readBlockAllocated(i), true)
            }
            table.removeBlockAllocation(2)
            assertEquals(table.meta.lastEmptyBlock, 2)

            for (i in 0..4) {
                if (i == 2)
                    assertEquals(table.readBlockAllocated(2), false)
                else
                    assertEquals(table.readBlockAllocated(i), true)
            }
            var block = PointerBlock(table, 2)
            assertEquals(block.isEmpty, true)
            block = PointerBlock(table, 4)

            assertEquals(block.fourthLevelAddress, 4)
            assertEquals(block.fifthLevelAddress, 4)
            assertEquals(block.sixthLevelAddress, 4)
            assertEquals(block.seventhLevelAddress, 4)

            block.fifthLevelAddress = 5
            block.writeToChannel()

            block = PointerBlock(table, 4)

            assertEquals(block.fifthLevelAddress, 5)

            for (i in 5..10) {
                table.allocateBlock {
                    PointerBlock(table, it).apply {
                        this.fourthLevelAddress = i
                        this.fifthLevelAddress = i
                        this.sixthLevelAddress = i
                        this.seventhLevelAddress = i

                        writeToChannel()
                    }
                }
            }
            block = PointerBlock(table, 2)
            assertEquals(block.isEmpty, false)
            assertEquals(block.fourthLevelAddress, 5)
            assertEquals(block.fifthLevelAddress, 5)
            assertEquals(block.sixthLevelAddress, 5)
            assertEquals(block.seventhLevelAddress, 5)

            block = PointerBlock(table, 6)
            assertEquals(block.isEmpty, false)
            assertEquals(block.fourthLevelAddress, 6)
            assertEquals(block.fifthLevelAddress, 6)
            assertEquals(block.sixthLevelAddress, 6)
            assertEquals(block.seventhLevelAddress, 6)

            assertEquals(table.meta.used, 11)

            for (i in 0..10) {
                table.removeBlockAllocation(i)
            }

            for (i in 0..10) {
                block = PointerBlock(table, i)
                assertEquals(block.isEmpty, true)
            }
            assertEquals(table.meta.used, 0)
            assertEquals(table.meta.lastEmptyBlock, 0)
        }
    }

    @Test
    fun dataBlock() {
        provideIndexTable { table ->
            for (i in 1..15) {
                table.allocateBlock {
                    DataBlock(table, it).apply {
                        previousBlockId = 1
                        dataIndex = 2
                        nextBlockId = 3
                        previousWordUnicode = 97
                        nextWordUnicode = 98

                        writeToChannel()
                    }
                }
            }

            for (i in 1..15) {
                val block = DataBlock(table, i)
                assertEquals(table.readBlockAllocated(i), true)
                assertEquals(block.isEmpty, false)
                assertEquals(block.previousBlockId, 1)
                assertEquals(block.dataIndex, 2)
                assertEquals(block.nextBlockId, 3)
                assertEquals(block.previousWordUnicode, 97)
                assertEquals(block.nextWordUnicode, 98)
            }

            testWordUnicode(table)
        }
    }

    fun testWordUnicode(table: IndexTable) {
        val blockId = table.allocateBlock()
        val maxUnicode = 65534
        val minUnicode = 32
        var block = DataBlock(table, blockId)
        block.nextWordUnicode = maxUnicode
        block.previousWordUnicode = minUnicode
        block.writeToChannel()

        block = DataBlock(table, blockId)
        assertEquals(block.isEmpty, false)
        assertEquals(block.previousWordUnicode, minUnicode)
        assertEquals(block.nextWordUnicode, maxUnicode)
    }
}
